// -----------------------------------------------------------------------
//  <copyright file="QueryRepository.cs" company="LiuliuSoft">
//      Copyright (c) 2022-2024 DaprPlus. All rights reserved.
//  </copyright>
//  <site>https://dapr.plus</site>
//  <last-editor>郭明锋</last-editor>
//  <last-date>2024/12/26 21:25</last-date>
// -----------------------------------------------------------------------

using Dapper;

using DaprPlus.Mapping;

using OSharp.Filter;

using OSharp.Threading;


namespace DaprPlus.EntityFrameworkCore;

/// <summary>
/// 通用只读仓储实现
/// </summary>
/// <typeparam name="TEntity"></typeparam>
public class QueryRepository<TEntity> : IQueryRepository<TEntity> where TEntity : class, IEntity
{
    protected readonly ISpecificationEvaluator SpecificationEvaluator;
    private readonly ICancellationTokenProvider _cancellationTokenProvider;

    /// <summary>
    /// 初始化一个<see cref="QueryRepository{TEntity}"/>类型的新实例
    /// </summary>
    public QueryRepository(IServiceProvider provider, bool slave = true)
    {
        _cancellationTokenProvider = provider.GetRequiredService<ICancellationTokenProvider>();
        SpecificationEvaluator = provider.GetRequiredService<ISpecificationEvaluator>();
        var dbContext = provider.GetRequiredService<IDbContext>();
        _DbContext = dbContext as DbContextBase ?? throw new InvalidOperationException("IDbContext必须是DbContextBase类型");
        if (slave)
        {
            _DbContext.ToSlave();
        }
    }

    /// <summary>
    /// 获取 当前数据上下文对象
    /// </summary>
    // ReSharper disable once InconsistentNaming
    protected DbContextBase _DbContext { get; }

    /// <summary>
    /// 获取 当前实体非跟踪查询数据集
    /// </summary>
    protected virtual IQueryable<TEntity> Query => _DbContext.Set<TEntity>().AsNoTracking();

    /// <summary>
    /// 获取 当前数据上下文对象
    /// </summary>
    public virtual IDbContext DbContext => _DbContext;

    /// <summary>
    /// 获取指定实体主键编号的实体
    /// </summary>
    /// <typeparam name="TKey">实体类型</typeparam>
    /// <param name="id">实体编号</param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public virtual async Task<TEntity?> FindAsync<TKey>(TKey id, CancellationToken cancellationToken = default)
    {
        cancellationToken = CancellationToken(cancellationToken);
        var dbSet = _DbContext.Set<TEntity>();
        var entity = await dbSet.FindAsync(id, cancellationToken);
        return entity;
    }

    /// <summary>
    /// 获取所有实体总数量
    /// </summary>
    public virtual async Task<int> GetCountAsync(CancellationToken cancellationToken = default)
    {
        cancellationToken = CancellationToken(cancellationToken);
        var count = await Query.CountAsync(cancellationToken);
        return count;
    }

    /// <summary>
    /// 获取所有符合规约的实体数量
    /// </summary>
    /// <param name="specification">规约</param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public virtual async Task<int> GetCountAsync(ISpecification<TEntity> specification, CancellationToken cancellationToken = default)
    {
        cancellationToken = CancellationToken(cancellationToken);
        var query = SpecificationEvaluator.Query(Query, specification);
        var count = await query.CountAsync(cancellationToken);
        return count;
    }

    /// <summary>
    /// 获取所有实体总数量
    /// </summary>
    public virtual async Task<long> GetLongCountAsync(CancellationToken cancellationToken = default)
    {
        cancellationToken = CancellationToken(cancellationToken);
        var count = await Query.LongCountAsync(cancellationToken);
        return count;
    }

    /// <summary>
    /// 获取所有符合规约的实体数量
    /// </summary>
    /// <param name="specification">规约</param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public virtual async Task<long> GetLongCountAsync(ISpecification<TEntity> specification, CancellationToken cancellationToken = default)
    {
        Check.NotNull(specification, nameof(specification));

        cancellationToken = CancellationToken(cancellationToken);
        var query = SpecificationEvaluator.Query(Query, specification);
        var count = await query.LongCountAsync(cancellationToken);
        return count;
    }

    /// <summary>
    /// 是否存在指定编号的实体
    /// </summary>
    /// <param name="id">实体编号</param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public async Task<bool> AnyAsync(long id, CancellationToken cancellationToken = default)
    {
        cancellationToken = CancellationToken(cancellationToken);
        var flag = await Query.AnyAsync(m => m.Id == id, cancellationToken);
        return flag;
    }

    /// <summary>
    /// 是否存在指定规约的实体
    /// </summary>
    /// <param name="specification">规约</param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public virtual async Task<bool> AnyAsync(ISpecification<TEntity> specification, CancellationToken cancellationToken = default)
    {
        Check.NotNull(specification, nameof(specification));

        cancellationToken = CancellationToken(cancellationToken);
        var query = SpecificationEvaluator.Query(Query, specification);
        var flag = await query.AnyAsync(cancellationToken);
        return flag;
    }

    /// <summary>
    /// 获取符合指定规约的第一个实体或Null
    /// </summary>
    /// <param name="specification">规约</param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public virtual async Task<TEntity?> GetAsync(ISpecification<TEntity> specification, CancellationToken cancellationToken = default)
    {
        Check.NotNull(specification, nameof(specification));

        cancellationToken = CancellationToken(cancellationToken);
        var query = SpecificationEvaluator.Query(Query, specification);
        var entity = await query.FirstOrDefaultAsync(cancellationToken);
        return entity;
    }

    /// <summary>
    /// 获取符合指定规约的第一个实体投影或默认值
    /// </summary>
    /// <typeparam name="TResult">投影类型</typeparam>
    /// <param name="specification">规约</param>
    /// <param name="selector">投影筛选器</param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public virtual async Task<TResult?> GetAsync<TResult>(ISpecification<TEntity> specification, Expression<Func<TEntity, TResult>> selector,
        CancellationToken cancellationToken = default)
    {
        Check.NotNull(specification, nameof(specification));

        cancellationToken = CancellationToken(cancellationToken);
        var query = SpecificationEvaluator.Query(Query, specification);
        var result = await query.Select(selector).FirstOrDefaultAsync(cancellationToken);
        return result;
    }

    /// <summary>
    /// 获取所有符合规约的实体集合
    /// </summary>
    /// <param name="specification">规约</param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public virtual async Task<List<TEntity>> GetAllAsync(ISpecification<TEntity> specification, CancellationToken cancellationToken = default)
    {
        Check.NotNull(specification, nameof(specification));

        cancellationToken = CancellationToken(cancellationToken);
        var query = SpecificationEvaluator.Query(Query, specification);
        var list = await query.ToListAsync(cancellationToken);
        return list;
    }

    /// <summary>
    /// 获取所有符合规约的实体投影集合
    /// </summary>
    /// <typeparam name="TResult">投影类型</typeparam>
    /// <param name="specification">规约</param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public async Task<List<TResult>> GetAllAsync<TResult>(ISpecification<TEntity> specification, CancellationToken cancellationToken = default)
    {
        Check.NotNull(specification, nameof(specification));

        cancellationToken = CancellationToken(cancellationToken);
        var query = SpecificationEvaluator.Query(Query, specification);
        var data = await query.ToOutput<TEntity, TResult>().ToListAsync(cancellationToken);
        return data;
    }

    /// <summary>
    /// 获取所有符合规约的实体投影集合
    /// </summary>
    /// <typeparam name="TResult">投影类型</typeparam>
    /// <param name="specification">规约</param>
    /// <param name="selector">投影筛选器</param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public virtual async Task<List<TResult>> GetAllAsync<TResult>(ISpecification<TEntity> specification, Expression<Func<TEntity, TResult>> selector,
        CancellationToken cancellationToken = default)
    {
        Check.NotNull(specification, nameof(specification));

        cancellationToken = CancellationToken(cancellationToken);
        var query = SpecificationEvaluator.Query(Query, specification);
        var list = await query.Select(selector).ToListAsync(cancellationToken);
        return list;
    }

    /// <summary>
    /// 获取所有符合规约的分页实体集合
    /// </summary>
    /// <param name="specification">规约</param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public async Task<PageResult<TEntity>> GetPageAsync(ISpecification<TEntity> specification,
        CancellationToken cancellationToken = default)
    {
        Check.NotNull(specification, nameof(specification));

        cancellationToken = CancellationToken(cancellationToken);
        var (query, total) = await SpecificationEvaluator.QueryAsync(Query, specification, cancellationToken);
        var data = await query.ToArrayAsync(cancellationToken);
        return new PageResult<TEntity>(data, total);
    }

    /// <summary>
    /// 获取所有符合规约的分页实体投影集合
    /// </summary>
    /// <typeparam name="TResult">投影类型</typeparam>
    /// <param name="specification">规约</param>
    /// <param name="selector">投影筛选器</param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public async Task<PageResult<TResult>> GetPageAsync<TResult>(ISpecification<TEntity> specification,
        Expression<Func<TEntity, TResult>> selector, CancellationToken cancellationToken = default)
    {
        Check.NotNull(specification, nameof(specification));
        Check.NotNull(selector, nameof(selector));

        cancellationToken = CancellationToken(cancellationToken);
        var (query, total) = await SpecificationEvaluator.QueryAsync(Query, specification, cancellationToken);
        var data = await query.Select(selector).ToArrayAsync(cancellationToken);
        return new PageResult<TResult>(data, total);
    }

    /// <summary>
    /// 获取所有符合规约的分页实体投影集合
    /// </summary>
    /// <typeparam name="TResult">投影类型</typeparam>
    /// <param name="specification">规约</param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public async Task<PageResult<TResult>> GetPageAsync<TResult>(ISpecification<TEntity> specification,
        CancellationToken cancellationToken = default)
    {
        Check.NotNull(specification, nameof(specification));

        cancellationToken = CancellationToken(cancellationToken);
        var (query, total) = await SpecificationEvaluator.QueryAsync(Query, specification, cancellationToken);
        var data = await query.ToOutput<TEntity, TResult>().ToArrayAsync(cancellationToken);
        return new PageResult<TResult>(data, total);
    }

    /// <summary>
    /// 查询指定SQL查询
    /// </summary>
    /// <typeparam name="TResult">结果类型</typeparam>
    /// <param name="sql">查询的SQL语句</param>
    /// <param name="param">SQL参数</param>
    /// <returns></returns>
    public async Task<List<TResult>> FromSql<TResult>(string sql, object? param = null)
    {
        var connection = _DbContext.Database.GetDbConnection();
        var list = await connection.QueryAsync<TResult>(sql, param);
        return list.ToList();
    }

    /// <summary>
    /// 查询指定SQL查询
    /// </summary>
    /// <param name="sql">查询的SQL语句</param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public async Task<List<TEntity>> FromSql(FormattableString sql, CancellationToken cancellationToken = default)
    {
        cancellationToken = CancellationToken(cancellationToken);
        var list = await _DbContext.Set<TEntity>().FromSql(sql).ToListAsync(cancellationToken);
        return list;
    }

    /// <summary>
    /// 获取任务取消标识
    /// </summary>
    protected CancellationToken CancellationToken(CancellationToken token)
    {
        return token != default ? token : _cancellationTokenProvider.Token;
    }
}
